// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package testing

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"sync"
)

// matcher对子测试和子基准测试的名称进行净化、唯一化和筛选。
type matcher struct {
	filter    filterMatch
	matchFunc func(pat, str string) (bool, error)

	mu sync.Mutex

	// 子名称用于消除子测试名称的重复。
	// 每个键都是与父测试的重复数据消除名称关联的子测试名称。
	// 每个值都是给定子测试名称
	// 已看到的出现次数的计数。
	subNames map[string]int32
}

type filterMatch interface {
	// matches使用给定的match函数根据接收方的模式字符串检查名称。
	matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)

	// 通过调用给定的匹配函数来检查接收器的模式字符串是否是有效的过滤器。
	verify(name string, matchString func(pat, str string) (bool, error)) error
}

// 如果所有模式字符串都在
// 序列中匹配，则simpleMatch匹配测试名称。
type simpleMatch []string

// alternationMatch匹配测试名称（如果其中一个选项匹配）。
type alternationMatch []filterMatch

// TODO:修复test_main以避免竞争并改进缓存，还允许
// 消除此互斥。
var matchMutex sync.Mutex

func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher {
	var impl filterMatch
	if patterns != "" {
		impl = splitRegexp(patterns)
		if err := impl.verify(name, matchString); err != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err)
			os.Exit(1)
		}
	}
	return &matcher{
		filter:    impl,
		matchFunc: matchString,
		subNames:  map[string]int32{},
	}
}

func (m *matcher) fullName(c *common, subname string) (name string, ok, partial bool) {
	name = subname

	m.mu.Lock()
	defer m.mu.Unlock()

	if c != nil && c.level > 0 {
		name = m.unique(c.name, rewrite(subname))
	}

	matchMutex.Lock()
	defer matchMutex.Unlock()

	if m.filter == nil {
		return name, true, false
	}

	// 我们每次都会检查完整的路径数组，以考虑模式包含“/”的情况。
	elem := strings.Split(name, "/")
	ok, partial = m.filter.matches(elem, m.matchFunc)
	return name, ok, partial
}

// 清除子名称清除匹配器的内部状态，可能会释放
// 内存。调用此函数后，T.Name可能会返回与之前的子测试相同的字符串。
func (m *matcher) clearSubNames() {
	m.mu.Lock()
	defer m.mu.Unlock()
	for key := range m.subNames {
		delete(m.subNames, key)
	}
}

func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
	for i, s := range name {
		if i >= len(m) {
			break
		}
		if ok, _ := matchString(m[i], s); !ok {
			return false, false
		}
	}
	return true, len(name) < len(m)
}

func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
	for i, s := range m {
		m[i] = rewrite(s)
	}
	// 在进行任何处理之前，请验证过滤器。
	for i, s := range m {
		if _, err := matchString(s, "non-empty"); err != nil {
			return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err)
		}
	}
	return nil
}

func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
	for _, m := range m {
		if ok, partial = m.matches(name, matchString); ok {
			return ok, partial
		}
	}
	return false, false
}

func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
	for i, m := range m {
		if err := m.verify(name, matchString); err != nil {
			return fmt.Errorf("alternation %d of %s", i, err)
		}
	}
	return nil
}

func splitRegexp(s string) filterMatch {
	a := make(simpleMatch, 0, strings.Count(s, "/"))
	b := make(alternationMatch, 0, strings.Count(s, "|"))
	cs := 0
	cp := 0
	for i := 0; i < len(s); {
		switch s[i] {
		case '[':
			cs++
		case ']':
			if cs--; cs < 0 { // 一个不匹配的']'是合法的。
				cs = 0
			}
		case '(':
			if cs == 0 {
				cp++
			}
		case ')':
			if cs == 0 {
				cp--
			}
		case '\\':
			i++
		case '/':
			if cs == 0 && cp == 0 {
				a = append(a, s[:i])
				s = s[i+1:]
				i = 0
				continue
			}
		case '|':
			if cs == 0 && cp == 0 {
				a = append(a, s[:i])
				s = s[i+1:]
				i = 0
				b = append(b, a)
				a = make(simpleMatch, 0, len(a))
				continue
			}
		}
		i++
	}

	a = append(a, s)
	if len(b) == 0 {
		return a
	}
	return append(b, a)
}

// unique为给定的父项和子项名称创建一个唯一的名称，如果需要，可以在其上附加一个或多个计数。
func (m *matcher) unique(parent, subname string) string {
	base := parent + "/" + subname

	for {
		n := m.subNames[base]
		if n < 0 {
			panic("subtest count overflow")
		}
		m.subNames[base] = n + 1

		if n == 0 && subname != "" {
			prefix, nn := parseSubtestNumber(base)
			if len(prefix) < len(base) && nn < m.subNames[prefix] {
				// 此测试的显式名称类似于“父/子名称#NN”，
				// 和#NN已用于第NN次出现的“父/子名称”。
				// 循环以添加消除歧义的后缀。
				continue
			}
			return base
		}

		name := fmt.Sprintf("%s#%02d", base, n)
		if m.subNames[name] != 0 {
			// 这是base的第n次出现，但名称“parent/subname#NN”
			// 与第一次出现的子测试*显式地*命名为
			// “parent/subname#NN”冲突。试试下一个号码。
			continue
		}

		return name
	}
}

// parseSubtestNumber将子测试名称拆分为“#%02d”格式的int32 
// 后缀（如果存在）和该后缀之前的前缀（始终）。
func parseSubtestNumber(s string) (prefix string, nn int32) {
	i := strings.LastIndex(s, "#")
	if i < 0 {
		return s, 0
	}

	prefix, suffix := s[:i], s[i+1:]
	if len(suffix) < 2 || (len(suffix) > 2 && suffix[0] == '0') {
		// 即使后缀是数字，也不可能是“%02”格式的输出
		// 字符串：它的数字太少或前导零太多。
		return s, 0
	}
	if suffix == "00" {
		if !strings.HasSuffix(prefix, "/") {
			// 对于以空
			// 字符串命名的子测试，我们仅使用“#00”作为后缀-如果子测试名称非空，则该后缀无效。
			return s, 0
		}
	}

	n, err := strconv.ParseInt(suffix, 10, 32)
	if err != nil || n < 0 {
		return s, 0
	}
	return prefix, int32(n)
}

// 重写将子名称重写为只有可打印字符且没有白色
// 空格。
func rewrite(s string) string {
	b := []byte{}
	for _, r := range s {
		switch {
		case isSpace(r):
			b = append(b, '_')
		case !strconv.IsPrint(r):
			s := strconv.QuoteRune(r)
			b = append(b, s[1:len(s)-1]...)
		default:
			b = append(b, string(r)...)
		}
	}
	return string(b)
}

func isSpace(r rune) bool {
	if r < 0x2000 {
		switch r {
		// 注意：与Unicode Z类不同。
		case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
			return true
		}
	} else {
		if r <= 0x200a {
			return true
		}
		switch r {
		case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
			return true
		}
	}
	return false
}
